#include "Surface.h"
#include <cstdint>
#include <random>

Surface::Surface() {
    this->weather = Climate();
    std::vector<Cell> temp;
    std::random_device rd{};
    std::mt19937 gen{rd()};
    std::normal_distribution d{50.0, 3.0};
    for (uint8_t i = 0; i != 70; i++) {
        for (uint8_t j = 0; j != 70; j++) {
            temp.push_back(Cell(static_cast<int>(std::round(d(gen)))));
        }
        this->earth.push_back(temp);
        temp.clear();
    }
}

std::vector<std::vector<Cell>> Surface::get_earth() const {
    return this->earth;
}


int Surface::max_height() const {
    long unsigned int size = this->earth.size();
    int max = 0;
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            if (this->earth[i][j].get_hight() > max) {
                max = this->earth[i][j].get_hight();
            }
        }
    }
    return max;
}
int Surface::min_height() const {
    long unsigned int size = this->earth.size();
    int min = max_height();
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            if (this->earth[i][j].get_hight() < min) {
                min = this->earth[i][j].get_hight();
            }
        }
    }
    return min;
}

int Surface::max_condition() const {
    long unsigned int size = this->earth.size();
    int max = 0;
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            if (this->earth[i][j].get_condition() > max) {
                max = this->earth[i][j].get_condition();
            }
        }
    }
    return max;
}

int Surface::min_condition() const {
    long unsigned int size = this->earth.size();
    int min = max_condition();
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            if (this->earth[i][j].get_condition() < min) {
                min = this->earth[i][j].get_condition();
            }
        }
    }
    return min;
}

void Surface::rain() {
    long unsigned int size = this->earth.size();
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            this->earth[i][j].set_condition(this->earth[i][j].get_condition() + this->weather.get_rain_volume() * this->rain_mod);
        }
    }
    for (int i = 0; static_cast<long unsigned int>(i) != size; i++) {
        for (int j = 0; static_cast<long unsigned int>(j) != size; j++) {
            if (i == 0 && j == 0) {
                if (this->earth[1][0].get_total() < this->earth[0][0].get_total()) {
                    this->earth[1][0].set_condition(this->earth[1][0].get_condition() + 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() - 1);
                }
                if (this->earth[0][1].get_total() < this->earth[0][0].get_total()) {
                    this->earth[0][1].set_condition(this->earth[0][1].get_condition() + 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() - 1);
                }
                if (this->earth[1][1].get_total() < this->earth[0][0].get_total()) {
                    this->earth[1][1].set_condition(this->earth[1][1].get_condition() + 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() - 1);
                }
            }
            else if (i == 0 && static_cast<long unsigned int>(j) == (size - 1)) {
                if (this->earth[1][size - 1].get_total() < this->earth[0][size - 1].get_total()) {
                    this->earth[1][size - 1].set_condition(this->earth[1][size - 1].get_condition() + 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() - 1);
                }
                if (this->earth[1][size - 2].get_total() < this->earth[0][size - 1].get_total()) {
                    this->earth[1][size - 2].set_condition(this->earth[1][size - 2].get_condition() + 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() - 1);
                }
                if (this->earth[0][size - 2].get_total() < this->earth[0][size - 1].get_total()) {
                    this->earth[0][size - 2].set_condition(this->earth[0][size - 2].get_condition() + 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() - 1);
                }
            } 
            else if (static_cast<long unsigned int>(i) == (size - 1) && static_cast<long unsigned int>(j) == (size - 1)) {
                if (this->earth[size - 2][size - 1].get_total() < this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 2][size - 1].set_condition(this->earth[size - 2][size - 1].get_condition() + 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() - 1);
                }
                if (this->earth[size - 1][size - 2].get_total() < this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 1][size - 2].set_condition(this->earth[size - 1][size - 2].get_condition() + 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() - 1);
                }
                if (this->earth[size - 2][size - 2].get_total() < this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 2][size - 2].set_condition(this->earth[size - 2][size - 2].get_condition() + 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() - 1);
                }
            } 
            else if (static_cast<long unsigned int>(i) == (size - 1) && j == 0) {
                if (this->earth[size - 2][0].get_total() < this->earth[size - 1][0].get_total()) {
                    this->earth[size - 2][0].set_condition(this->earth[size - 2][0].get_condition() + 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() - 1);
                }
                if (this->earth[size - 1][1].get_total() < this->earth[size - 1][0].get_total()) {
                    this->earth[size - 1][1].set_condition(this->earth[size - 1][1].get_condition() + 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() - 1);
                }
                if (this->earth[size - 2][1].get_total() < this->earth[size - 1][0].get_total()) {
                    this->earth[size - 2][1].set_condition(this->earth[size - 2][1].get_condition() + 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() - 1);
                }
            }
            else {
                for (int a = -1; a != 1; a++) {
                    for (int b = -1; b != 1; b++) {
                        if (!((i == 0 && a == -1) || 
                             (j == 0 && b == -1) ||
                             (static_cast<unsigned long int>(i) == (size - 1) && a == 1) ||
                             (static_cast<long unsigned int>(j) == (size - 1) && b == 1))) {
                            if (this->earth[static_cast<long unsigned int>(i + a)][static_cast<long unsigned int>(j + b)].get_total() < this->earth[static_cast<long unsigned int>(i)][static_cast<long unsigned int>(j)].get_total()) {
                                this->earth[static_cast<long unsigned int>(i + a)][static_cast<unsigned long int>(j + b)].set_condition(this->earth[static_cast< unsigned long int>(i + a)][static_cast<unsigned long int>(j + b)].get_condition() + 1);
                                this->earth[static_cast<long unsigned int>(i)][static_cast<long unsigned int>(j)].set_condition(this->earth[static_cast<long unsigned int>(i)][static_cast<long unsigned int>(j)].get_condition() - 1);
                            }
                        }
                }
            }
            }
        }
    }
    for (int i = 0; static_cast<long unsigned int>(i) != size; i++) {
        for (int j = 0; static_cast<long unsigned int>(j) != size; j++) {
            if (i == 0 && j == 0) {
                if (this->earth[1][0].get_total() > this->earth[0][0].get_total()) {
                    this->earth[1][0].set_condition(this->earth[1][0].get_condition() - 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() + 1);
                }
                if (this->earth[0][1].get_total() > this->earth[0][0].get_total()) {
                    this->earth[0][1].set_condition(this->earth[0][1].get_condition() - 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() + 1);
                }
                if (this->earth[1][1].get_total() > this->earth[0][0].get_total()) {
                    this->earth[1][1].set_condition(this->earth[1][1].get_condition() - 1);
                    this->earth[0][0].set_condition(this->earth[0][0].get_condition() + 1);
                }
            }
            else if (i == 0 && static_cast<long unsigned int>(j) == (size - 1)) {
                if (this->earth[1][size - 1].get_total() > this->earth[0][size - 1].get_total()) {
                    this->earth[1][size - 1].set_condition(this->earth[1][size - 1].get_condition() - 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() + 1);
                }
                if (this->earth[1][size - 2].get_total() > this->earth[0][size - 1].get_total()) {
                    this->earth[1][size - 2].set_condition(this->earth[1][size - 2].get_condition() - 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() + 1);
                }
                if (this->earth[0][size - 2].get_total() > this->earth[0][size - 1].get_total()) {
                    this->earth[0][size - 2].set_condition(this->earth[0][size - 2].get_condition() - 1);
                    this->earth[0][size - 1].set_condition(this->earth[0][size - 1].get_condition() + 1);
                }
            } 
            else if (static_cast<long unsigned int>(i) == (size - 1) && static_cast<long unsigned int>(j) == (size - 1)) {
                if (this->earth[size - 2][size - 1].get_total() > this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 2][size - 1].set_condition(this->earth[size - 2][size - 1].get_condition() - 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() + 1);
                }
                if (this->earth[size - 1][size - 2].get_total() > this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 1][size - 2].set_condition(this->earth[size - 1][size - 2].get_condition() - 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() + 1);
                }
                if (this->earth[size - 2][size - 2].get_total() > this->earth[size - 1][size - 1].get_total()) {
                    this->earth[size - 2][size - 2].set_condition(this->earth[size - 2][size - 2].get_condition() - 1);
                    this->earth[size - 1][size - 1].set_condition(this->earth[size - 1][size - 1].get_condition() + 1);
                }
            } 
            else if (static_cast<long unsigned int>(i) == (size - 1) && j == 0) {
                if (this->earth[size - 2][0].get_total() > this->earth[size - 1][0].get_total()) {
                    this->earth[size - 2][0].set_condition(this->earth[size - 2][0].get_condition() - 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() + 1);
                }
                if (this->earth[size - 1][1].get_total() > this->earth[size - 1][0].get_total()) {
                    this->earth[size - 1][1].set_condition(this->earth[size - 1][1].get_condition() - 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() + 1);
                }
                if (this->earth[size - 2][1].get_total() > this->earth[size - 1][0].get_total()) {
                    this->earth[size - 2][1].set_condition(this->earth[size - 2][1].get_condition() - 1);
                    this->earth[size - 1][0].set_condition(this->earth[size - 1][0].get_condition() + 1);
                }
            }
            else {
                for (int a = -1; a != 1; a++) {
                    for (int b = -1; b != 1; b++) {
                        if (!((i == 0 && a == -1) || 
                             (j == 0 && b == -1) ||
                             (static_cast<long unsigned int>(i) == (size - 1) && a == 1) ||
                             (static_cast<long unsigned int>(j) == (size - 1) && b == 1))) {
                            if (this->earth[static_cast<unsigned long int>(i + a)][static_cast<unsigned long int>(j + b)].get_total() > this->earth[static_cast<unsigned long int>(i)][static_cast<long unsigned int>(j)].get_total()) {
                                this->earth[static_cast<long unsigned int>(i + a)][static_cast<unsigned long int>(j + b)].set_condition(this->earth[static_cast<long unsigned int>(i + a)][static_cast<unsigned long int>(j + b)].get_condition() - 1);
                                this->earth[static_cast<long unsigned int>(i)][static_cast<long unsigned int>(j)].set_condition(this->earth[static_cast<long unsigned int>(i)][static_cast<unsigned long int>(j)].get_condition() + 1);
                            }
                        }
                }
            }
            }
        }
    }
}

Surface::Surface(const int &min_height, const int &max_height, const int &rain_mod) {
    this->rain_mod = rain_mod;
    this->weather = Climate();
    std::vector<Cell> temp;
    std::random_device rd{};
    std::mt19937 gen{rd()};
    std::normal_distribution d{static_cast<double>((max_height - min_height)) /
    2.0 + static_cast<double>(min_height),
    static_cast<double>((max_height - min_height)) / 10.0};
    for (uint8_t i = 0; i != 70; i++) {
        for (uint8_t j = 0; j != 70; j++) {
            temp.push_back(Cell(static_cast<int>(std::round(d(gen)))));
        }
        this->earth.push_back(temp);
        temp.clear();
    }
}

int Surface::get_total_condition() const {
    long unsigned int size = this->earth.size();
    int total = 0;
    for (long unsigned int i = 0; i != size; i++) {
        for (long unsigned int j = 0; j != size; j++) {
            total += this->earth[i][j].get_condition();
        }
    }
    return total;
}
